package com.gitee.l0km.aocache;

import java.lang.reflect.Type;

import org.aspectj.lang.ProceedingJoinPoint;

import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Optional;

/**
 * 基于 {@link ThrowingFunctionCached}实现的调用结果缓存，用于保存方法调用的结果数据。
 * <p>
 * 该类主要功能：
 * <ul>
 *   <li>缓存方法调用结果，避免重复计算</li>
 *   <li>提供调试信息输出功能</li>
 *   <li>记录切面调用耗时</li>
 * </ul>
 *
 * @author guyadong
 * @see ThrowingFunctionCached
 */
class InvokeCache extends ThrowingFunctionCached<InvokeKey, Object>
 	implements ThrowingFunction<InvokeKey, Object,Throwable>{
	/**
	 * 是否输出调试信息
	 * <p>
	 * 当设置为true时，会输出方法调用信息
	 */
	final boolean debugOutput;
	/**
	 * 是否输出详细调试信息
	 * <p>
	 * 当设置为true时，会输出更详细的调用信息，包括InvokeKey的详细信息
	 */
	final boolean debugOutputDetail;
	/**
	 * 是否输出切面调用耗时信息
	 * <p>
	 * 当设置为true时，会记录并输出方法调用的耗时情况
	 */
	final boolean outputAroundCost;
	private final AocacheLogger logger;

    /**
     * 用于处理方法调用参数的安全策略数组<p>
     * 该数组中的每个元素都是一个{@link Function}对象，用于对方法调用的参数进行安全处理。<br>
     * 如果传入的参数安全策略数组为null，则初始化为空数组。<br>
     */
    @SuppressWarnings("rawtypes")
	private final Function[] argWrappers;

    /**
     * 用于处理方法调用返回值的安全策略<p>
     * 该对象是一个{@link Function}对象，用于对方法调用的返回值进行安全处理。<br>
     * 如果传入的返回值安全策略为null，则使用默认的安全策略 SafeStrategy.NONE。<br>
     */
    @SuppressWarnings("rawtypes")
	private final Function returnWrapper;
    /**
     * 方法返回值的安全策略。<p>
     * 该策略用于确保方法返回值在处理过程中的安全性。<br>
     */
    private final SafeStrategy returnSafeStrategy;
	/**
	 * 方法返回值的类型<p>
	 * 该类型信息用于在处理返回值安全策略时，对返回值进行相应的类型转换和处理。<br>
	 * 例如，在使用克隆或深度克隆策略时，会根据该类型信息进行操作。
	 */
	private final Type returnType;
	@SuppressWarnings({ "rawtypes", "unchecked" })
	InvokeCache(ThrowingFunctionCached.Builder builder, 
			boolean debugOutput,
			boolean debugOutputDetail, 
			boolean outputAroundCost, 
			AocacheLogger logger,
			Function<?, ?>[] argWrappers,
			Function<?, ?>  returnWrapper,
			SafeStrategy returnSafeStrategy,
			Optional<Type>  returnType) {
		super(builder.cacheBuilder);
		this.debugOutput = debugOutput;
		this.debugOutputDetail = debugOutputDetail;
		this.outputAroundCost = outputAroundCost;
		this.logger = logger;
		/**
		 * 初始化参数安全策略数组<p>
		 * 如果传入的参数安全策略数组为null，则初始化为空数组。<br>
		 */
		this.argWrappers = null == argWrappers ? AocacheConstant.EMPTY_FUNS_ARRAY : argWrappers;
		/**
		 * 初始化返回值安全策略<p>
		 * 如果传入的返回值安全策略为null，则使用默认的安全策略 SafeStrategy.NONE。<br>
		 */
		this.returnWrapper = null == returnWrapper ? Functions.identity() : returnWrapper;
		/**
		 * 初始化返回值安全策略<p>
		 * 如果传入的返回值安全策略为null，则使用默认的安全策略 SafeStrategy.NONE。<br>
		 */
		this.returnSafeStrategy = null == returnSafeStrategy ? SafeStrategy.NONE : returnSafeStrategy;
		this.returnType = returnType.orNull();
	}

	/**
	 * 执行方法调用并缓存结果
	 * <p>
	 * 主要功能：
	 * <ul>
	 *   <li>递增proceedCounter计数器</li>
	 *   <li>初始化aroundCounter</li>
	 *   <li>根据配置输出调试信息</li>
	 *   <li>执行实际方法调用并返回结果</li>
	 * </ul>
	 *
	 * @param t 包含方法调用信息的InvokeKey对象
	 * @return 方法调用的返回结果
	 * @throws Throwable 方法调用可能抛出的异常
	 */
	@SuppressWarnings("unchecked")
	@Override
	public Object apply(InvokeKey t) throws Throwable {
		/** 
		 * 当JoinPoint为null时，抛出异常，避免缓存中存在null值
		 * 一般情况下，JoinPoint不会为null，除非使用了@AoUpdateable注解的方法，
	  	 */
		ProceedingJoinPoint jp;
		if(null == (jp = t.getJoinPoint())) {
			throw new IllegalStateException("JoinPoint is null for key: " + t.toSimpleString());
		}
		t.proceedCounter.incrementAndGet();
		if(debugOutput) {
			if (debugOutputDetail) {
				logger.log("InvokeKey:{}", t);
			}
		}
		/** argWrappers数组不为空代表需要对参数进行包装 */
		if (argWrappers.length > 0 && !t.argWrapped) {
			for(int i = 0; i < argWrappers.length; i++) {
				t.args[i] = argWrappers[i].apply(t.args[i]);
			}
			t.argWrapped = true;
		}
		ContextHolder.computeInvocation();
		long startTime = PerformanceProfiler.enableLog ? System.nanoTime() : 0L;
		Object result;
		try {
			result = jp.proceed();
		} finally {
			if (PerformanceProfiler.enableLog) {
				/** 记录计算调用纯耗时(纳秒) */
				t.proceedRealCost.set(System.nanoTime() - startTime);
			}
		}
		if(SafeStrategy.IMMUTABLE.equals(returnSafeStrategy)) {
			/** 如果返回值安全策略为IMMUTABLE，则将返回值转换为不可变对象 */
			result = returnWrapper.apply(result);
		}
		return result;
	}

	/**
	 * 将缓存对象转换为实际的值。
	 * <p>
	 * 该方法会根据返回值的安全策略对缓存对象进行处理，
	 * 若安全策略为 {@code CLONE} 或 {@code DEEPCLONE}，则会使用返回值包装器对缓存对象进行处理。
	 * </p>
	 * 
	 * @param cached 缓存中的对象
	 * @return 转换后的实际值，可能为 {@code null}
	 * @throws Throwable 转换过程中可能抛出的异常
	 */
	@SuppressWarnings("unchecked")
	@Override
	protected Object asValue(Object cached) throws Throwable {
		Object value = super.asValue(cached);
		if (null != value && SafeStrategy.NONE != this.returnSafeStrategy) {
			/**
			 * 如果返回值安全策略不为NONE，则优先使用局部上下文的返回值安全策略。
			 * 因为大部分场景下，返回值安全策略为NONE，
			 * 所以出于性能考虑，为了避免每次都要获取局部上下文的返回值安全策略，
			 * 为 NONE 的情况，直接使用类变量的返回值安全策略。忽略局部上下文的返回值安全策略。
			 * 非 NONE 的情况，才会获取局部上下文的返回值安全策略。
			 */
			SafeStrategy strategy = ContextHolder.getReturnSafeStrategy(this.returnSafeStrategy);
			switch (strategy) {
			case CLONE:
			case DEEPCLONE:
			case MUTABLECLONE:
				if(strategy != this.returnSafeStrategy){
					/**
					 * 如果局部上下文的返回值安全策略与类变量的返回值安全策略不同，
					 * 则根据返回值类型，使用局部上下文的返回值安全策略对返回值进行处理。 
					 */
					return strategy.wrap(value, returnType);
				}else{
					value = returnWrapper.apply(value);
				}
				break;
			default:
				break;
			}
		}
		return value;
	}

	@Override
	protected void clear() {
		if (debugOutput) {
			logger.log("CLEAN Invoke Cache, size:{} of {}", cache.size(), this);
		}
		super.clear();
	}
}
